
/**
 * @file:       param_lib.hpp
 * @author:     WubinWang
 * @contact:    wubinstu@163.com
 * @date:       2025-01-01
 * @license:    MIT License
 *
 * Copyright (c) 2025 WubinWang
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * DISCLAIMER:
 * 1. User acknowledges all application risks and consequences arising from usage
 *    of this software are exclusively borne by the user.
 * 2. The original author shall not be held liable for any direct, indirect,
 *    incidental, special, exemplary or consequential damages.
 * 3. No warranty of fitness for particular purpose or non-infringement is provided. */


#ifndef __PARAM_LIB_HPP_
#define __PARAM_LIB_HPP_

#include <array>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <functional>
#include <limits>
#include <string>

#pragma pack(push, 1)

namespace paramfw {

    constexpr const static inline auto kMagic     = 0x504D5A21U;  // 魔数
    constexpr const static inline auto kVersion   = 0x0102U;      // v1.2
    constexpr const static inline auto kMaxName   = 24;           // 参数名最大长度
    constexpr const static inline auto kMaxSize   = 8;            // 参数数值最大长度
    constexpr const static inline auto kMaxParams = 256;          // 最大参数数

    static_assert (kMaxName == 0x18, "params size changed, please make sure that is right");
    static_assert (kMaxSize == 0x08, "params size changed, please make sure that is right");

    // 存储类型抽象接口
    struct storage {
        virtual ~storage ()                                                   = default;
        virtual bool     init ()                                              = 0;
        virtual bool     read (uint32_t off, void * buf, uint32_t len)        = 0;
        virtual bool     write (uint32_t off, const void * buf, uint32_t len) = 0;
        virtual bool     erase ()                                             = 0;  // 整幅擦除
        virtual uint32_t size () const                                        = 0;
    };

    // 参数数值类型
    enum class type : uint8_t {
        nil = 0x00,
        s08 = 0x01,
        u08 = 0x02,
        s16 = 0x03,
        u16 = 0x04,
        s32 = 0x05,
        u32 = 0x06,
        s64 = 0x07,
        u64 = 0x08,
        f32 = 0x09,
        f64 = 0x0A,
        str = 0x0B,
    };

    // 参数权限标志
    enum class flags : uint8_t {
        F_NONE          = 0b0000,
        F_RD            = 0b0001,
        F_WR            = 0b0010,
        F_PERSIST       = 0b0100,  // 持久化
        F_RD_WR         = 0b0011,
        F_PERSIST_RD    = 0b0101,
        F_PERSIST_WR    = 0b0110,
        F_PERSIST_RD_WR = 0b0111,
    };

    // 参数名称
    template <uint8_t N>
    struct str {
        char _buf[N] {};  // 零填充
        constexpr str () = default;

        constexpr str (const char * s) {
            auto i = 0;
            for (; s && s[i] && i < N - 1; ++i) this->_buf[i] = s[i];
            for (; i < N; ++i) this->_buf[i] = 0;
        }

        const char * c_str () const { return this->_buf; }
    };

    // 参数数值
    struct value {
        type _type;

        union {
            int8_t   s08;
            uint8_t  u08;
            int16_t  s16;
            uint16_t u16;
            int32_t  s32;
            uint32_t u32;
            int64_t  s64;
            uint64_t u64;
            float    f32;
            double   f64;
            char     str[kMaxSize];
            char     buf[kMaxSize];
        };
    };

    // 参数元数据
    struct meta {

        constexpr meta () {}

        constexpr meta (const char * _n, type _t, flags _f) noexcept :
            _name {_n}, _type {_t}, _flags {_f} {}

        constexpr meta (const char * _n, type _t, flags _f, const value & _vd) noexcept :
            meta {_n, _t, _f} { this->_default = _vd; }

        constexpr meta (str<kMaxName> _n, type _t, flags _f, const value & _vd) noexcept :
            _name {_n}, _type {_t}, _flags {_f}, _default {_vd} {}

        str<kMaxName> _name {};     // 参数名
        type          _type {};     // 类型
        flags         _flags {};    // 标志
        value         _default {};  // 默认值
    };

    // 参数节点
    struct node {
        meta  _meta {};     // 描述
        value _current {};  // 当前值
    };

    extern flags operator| (flags a, flags b);
    extern flags operator& (flags a, flags b);
    extern bool  operator== (const value & a, const value & b);
    extern bool  operator!= (const value & a, const value & b);

    // 注册表
    class registry {

        registry ()                             = default;
        registry (const registry &)             = delete;
        registry & operator= (const registry &) = delete;

      public:

        static auto getInstance () -> registry * {
            static registry reg;
            return &reg;
        }

        bool save ();
        bool load ();

        using callback = void (*) (const meta & _meta, const value & _old, const value & _new);

        bool subscribe (callback cb);

        auto count () const { return this->_count; }

        auto flash () -> storage *& { return this->_storage; }

        bool init (const meta _m[], uint32_t _c, storage * _s = nullptr);
        void reset_to_default ();

        const node * find (const char * name) const;

        node * find (const char * name) { return const_cast<node *> ((const_cast<const registry *> (this))->find (name)); }

        bool get (const char * name, value & out) const;
        bool get_as (const char * name, int8_t & out) const;
        bool get_as (const char * name, uint8_t & out) const;
        bool get_as (const char * name, int16_t & out) const;
        bool get_as (const char * name, uint16_t & out) const;
        bool get_as (const char * name, int32_t & out) const;
        bool get_as (const char * name, uint32_t & out) const;
        bool get_as (const char * name, int64_t & out) const;
        bool get_as (const char * name, uint64_t & out) const;
        bool get_as (const char * name, float & out) const;
        bool get_as (const char * name, double & out) const;
        bool get_as (const char * name, char (&out)[kMaxSize]) const;
        bool get_as (const char * name, uint8_t (&out)[kMaxSize]) const;


        bool set (const char * name, const value & nv);
        bool set_as (const char * name, const int8_t & out);
        bool set_as (const char * name, const uint8_t & out);
        bool set_as (const char * name, const int16_t & out);
        bool set_as (const char * name, const uint16_t & out);
        bool set_as (const char * name, const int32_t & out);
        bool set_as (const char * name, const uint32_t & out);
        bool set_as (const char * name, const int64_t & out);
        bool set_as (const char * name, const uint64_t & out);
        bool set_as (const char * name, const float & out);
        bool set_as (const char * name, const double & out);
        bool set_as (const char * name, char * const & out);
        bool set_as (const char * name, const char * const & out);
        bool set_as (const char * name, uint8_t * const & out);

      protected:

        bool save_impl ();
        bool load_impl ();

        void notify (const meta & m, const value & _old, const value & _new);


      private:
        std::array<node, kMaxParams> _nodes {};
        uint32_t                     _count {0};
        std::array<callback, 16>     _cbs {};
        storage *                    _storage {nullptr};
    };

    constexpr inline meta define (const char * _name, char * _default, flags _flag = flags::F_PERSIST_RD_WR) {
        meta  m {_name, type::str, _flag, value {._type = type::str, .str = {0}}};
        char *dst = m._default.str, *src = _default;
        while ((*dst++ = *src++));
        return m;
    }

    constexpr inline meta define (const char * _name, const char * _default, flags _flag = flags::F_PERSIST_RD_WR) { return define (_name, const_cast<char *> (_default), _flag); }

    constexpr inline meta define (const char * _name, int8_t _default, flags _flag = flags::F_PERSIST_RD_WR) { return meta {_name, type::s08, _flag, value {._type = type::s08, .s08 = _default}}; }

    constexpr inline meta define (const char * _name, uint8_t _default, flags _flag = flags::F_PERSIST_RD_WR) { return meta {_name, type::u08, _flag, value {._type = type::u08, .u08 = _default}}; }

    constexpr inline meta define (const char * _name, int16_t _default, flags _flag = flags::F_PERSIST_RD_WR) { return meta {_name, type::s16, _flag, value {._type = type::s16, .s16 = _default}}; }

    constexpr inline meta define (const char * _name, uint16_t _default, flags _flag = flags::F_PERSIST_RD_WR) { return meta {_name, type::u16, _flag, value {._type = type::u16, .u16 = _default}}; }

    constexpr inline meta define (const char * _name, int32_t _default, flags _flag = flags::F_PERSIST_RD_WR) { return meta {_name, type::s32, _flag, value {._type = type::s32, .s32 = _default}}; }

    constexpr inline meta define (const char * _name, uint32_t _default, flags _flag = flags::F_PERSIST_RD_WR) { return meta {_name, type::u32, _flag, value {._type = type::u32, .u32 = _default}}; }

    constexpr inline meta define (const char * _name, int64_t _default, flags _flag = flags::F_PERSIST_RD_WR) { return meta {_name, type::s64, _flag, value {._type = type::s64, .s64 = _default}}; }

    constexpr inline meta define (const char * _name, uint64_t _default, flags _flag = flags::F_PERSIST_RD_WR) { return meta {_name, type::u64, _flag, value {._type = type::u64, .u64 = _default}}; }

    constexpr inline meta define (const char * _name, float _default, flags _flag = flags::F_PERSIST_RD_WR) { return meta {_name, type::f32, _flag, value {._type = type::f32, .f32 = _default}}; }

    constexpr inline meta define (const char * _name, double _default, flags _flag = flags::F_PERSIST_RD_WR) { return meta {_name, type::f64, _flag, value {._type = type::f64, .f64 = _default}}; }

    // 用于打印测试的回调函数
    extern void on_param_change (const meta & m, const value & oldv, const value & newv);


}  // namespace paramfw

constexpr inline auto operator"" _s08 (unsigned long long val) noexcept -> std::int8_t { return static_cast<std::int8_t> (val); }

constexpr inline auto operator"" _u08 (unsigned long long val) noexcept -> std::uint8_t { return static_cast<std::uint8_t> (val); }

constexpr inline auto operator"" _s16 (unsigned long long val) noexcept -> std::int16_t { return static_cast<std::int16_t> (val); }

constexpr inline auto operator"" _u16 (unsigned long long val) noexcept -> std::uint16_t { return static_cast<std::uint16_t> (val); }

constexpr inline auto operator"" _s32 (unsigned long long val) noexcept -> std::int32_t { return static_cast<std::int32_t> (val); }

constexpr inline auto operator"" _u32 (unsigned long long val) noexcept -> std::uint32_t { return static_cast<std::uint32_t> (val); }

constexpr inline auto operator"" _s64 (unsigned long long val) noexcept -> std::int64_t { return static_cast<std::int64_t> (val); }

constexpr inline auto operator"" _u64 (unsigned long long val) noexcept -> std::uint64_t { return static_cast<std::uint64_t> (val); }

constexpr inline auto operator"" _f32 (long double val) noexcept -> float { return static_cast<float> (val); }

constexpr inline auto operator"" _f64 (long double val) noexcept -> double { return static_cast<double> (val); }

#pragma pack(pop)


#endif
